package org.eclipseuseradmin.ui.utils;

import org.eclipse.core.databinding.AggregateValidationStatus;
import org.eclipse.core.databinding.Binding;
import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.databinding.UpdateValueStrategy;
import org.eclipse.core.databinding.observable.ChangeEvent;
import org.eclipse.core.databinding.observable.IChangeListener;
import org.eclipse.core.databinding.observable.IObservable;
import org.eclipse.core.databinding.observable.Realm;
import org.eclipse.core.databinding.observable.value.AbstractObservableValue;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.swt.widgets.Control;
import org.eclipse.ui.forms.IMessageManager;
import org.eclipse.ui.forms.editor.FormPage;

public class FormPageSupport {

	private EditorSupport editorSupport;

	private FormPage page;

	private DataBindingContext bindingContext;

	private FormPageSupport(FormPage page, DataBindingContext bindingContext) {

		init(editorSupport, page, bindingContext);
	}

	private FormPageSupport(EditorSupport editorSupport, FormPage page,
			DataBindingContext bindingContext) {

		init(editorSupport, page, bindingContext);

		if (editorSupport != null) {
			bindingContext.bindValue(new AggregateValidationObservableValue(),
					new AggregateValidationStatus(bindingContext.getBindings(),
							AggregateValidationStatus.MERGED),
					new UpdateValueStrategy(UpdateValueStrategy.POLICY_NEVER),
					new UpdateValueStrategy());
		}
	}

	private void init(EditorSupport editorSupport, FormPage page,
			DataBindingContext bindingContext) {

		this.editorSupport = editorSupport;
		this.page = page;
		this.bindingContext = bindingContext;
	}

	public static FormPageSupport create(FormPage page,
			DataBindingContext bindingContext) {
		return new FormPageSupport(page, bindingContext);
	}

	public static FormPageSupport create(EditorSupport editorSupport,
			FormPage page, DataBindingContext bindingContext) {
		return new FormPageSupport(editorSupport, page, bindingContext);
	}

	public void bindMessage(Binding binding, Control control) {

		bindingContext.bindValue(new MessageObservableValue(page
				.getManagedForm().getMessageManager(), control), binding
				.getValidationStatus(), new UpdateValueStrategy(
				UpdateValueStrategy.POLICY_NEVER), new UpdateValueStrategy());
	}

	public void bindDirtyState(IObservable target) {

		if (editorSupport != null) {
			target.addChangeListener(new IChangeListener() {
				public void handleChange(ChangeEvent event) {

					editorSupport.setDirty(true);
				}
			});
		}
	}

	private class AggregateValidationObservableValue extends
			AbstractObservableValue {

		@Override
		protected Object doGetValue() {
			return null;
		}

		@Override
		protected void doSetValue(Object value) {

			IStatus status = (IStatus) value;
			if (status.isOK()) {
				editorSupport.removeError(this);
			} else {
				editorSupport.addError(this, status);
			}
		}

		public Object getValueType() {
			return IStatus.class;
		}
	}

	private class MessageObservableValue extends AbstractObservableValue {

		private IStatus status;
		private Control control;
		private IMessageManager msgManager;

		/**
		 * @param msgManager
		 *            the IMessageManager, never <code>null</code>
		 * @param control
		 *            the control which the status belongs to or
		 *            <code>null</code> when the status is a form wide status
		 */
		public MessageObservableValue(IMessageManager msgManager,
				Control control) {
			super();
			init(msgManager, control);
		}

		/**
		 * @param realm
		 * @param msgManager
		 *            the IMessageManager, never <code>null</code>
		 * @param control
		 *            the control which the status belongs to or
		 *            <code>null</code> when the status is a form wide status
		 * @see AbstractObservableValue#AbstractObservableValue(Realm)
		 */
		public MessageObservableValue(Realm realm, IMessageManager msgManager,
				Control control) {
			super(realm);
			init(msgManager, control);
		}

		private void init(IMessageManager msgManager, Control control) {
			this.msgManager = msgManager;
			this.control = control;
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @seeorg.eclipse.core.databinding.observable.value.IObservableValue#
		 * getValueType()
		 */
		public Object getValueType() {
			return IStatus.class;
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see
		 * org.eclipse.core.databinding.observable.value.AbstractObservableValue
		 * #doGetValue()
		 */
		@Override
		protected Object doGetValue() {
			return status;
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see
		 * org.eclipse.core.databinding.observable.value.AbstractObservableValue
		 * #doSetValue(java.lang.Object)
		 */
		@Override
		protected void doSetValue(Object value) {
			Assert.isLegal(value instanceof IStatus);
			setMessage((IStatus) value, control);
		}

		/**
		 * Sets the message for the given control and status
		 * 
		 * @param status
		 * @param control
		 *            the control in the form or <code>null</code> for a form
		 *            wide message
		 */
		public void setMessage(IStatus status, Control control) {
			if (status.isOK()) {
				removeMsg(control);
			} else {
				addMsg(status, control);
			}
		}

		private void addMsg(IStatus status, Control control) {
			String messageText = status.getMessage();
			int type;
			switch (status.getSeverity()) {
			case IStatus.CANCEL:
				type = IMessageProvider.ERROR;
				break;
			case IStatus.ERROR:
				type = IMessageProvider.ERROR;
				break;
			case IStatus.WARNING:
				type = IMessageProvider.WARNING;
				break;
			case IStatus.INFO:
				type = IMessageProvider.INFORMATION;
				break;
			case IStatus.OK:
				type = IMessageProvider.NONE;
				break;
			default:
				type = IMessageProvider.NONE;
				break;
			}

			if (control == null) {
				msgManager.addMessage(this, messageText, status, type);
			} else {
				msgManager.addMessage(this, messageText, status, type, control);
			}

		}

		private void removeMsg(Control control) {
			if (control == null) {
				msgManager.removeMessage(this);
			} else {
				msgManager.removeMessage(this, control);
			}
		}

	}

}
